package com.audio.service.impl;

import ForgedDetection.ForgedDetection;
import SplitDetection.SplitDetection;
import T0.Class1;
import com.alibaba.fastjson2.JSON;
import com.audio.constant.AlgorithmConstant;
import com.audio.constant.ResultFlag;
import com.audio.context.BaseContext;
import com.audio.mapper.UserMapper;
import com.audio.pojo.dto.TaskDTO;
import com.audio.pojo.entity.Log;
import com.audio.pojo.entity.User;
import com.audio.service.FileService;
import com.audio.utils.ForgedDetectionUtil;
import com.audio.websocket.AudioProcessingWebSocketHandler;
import com.mathworks.toolbox.javabuilder.MWException;
import com.mathworks.toolbox.javabuilder.MWLogicalArray;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class FileServiceImpl implements FileService {

    @Resource
    private UserMapper userMapper;


    // 获取当前项目文件夹
    static String currentDirectory = System.getProperty("user.dir");
    // 上传到本地的文件路径
    private static final String UPLOAD_DIR = Paths.get(currentDirectory, "localAudio") + "\\";
    // 分割后文件临时保存路径
    private static final String TEMP_DIR = Paths.get(currentDirectory, "temp").toString();
    // 分割后每段音频长度(cqcc为500， ezcsda为5000)
    private static int SEGMENT_TIME_MS = 500;

    // 选择的算法名(默认cqcc)
    private static String METHOD = AlgorithmConstant.METHOD_EZCS;
    // 算法阈值（默认1.0）
    private static double THRESHOLD = 0.85;
    // 标志值（默认1，如果设置为0将返回PCC值，后续可用于计算概率）
    private static int FLAGE = 1;
    //帧长 （帧长，单位：秒，标量，默认为 0.015 秒）：每帧的时长。默认值为 15ms。
    private double FRAME_DURATION = 0.015;
    // 帧移 （帧移，单位：秒，标量，默认为 0.005 秒）：每帧的移位量。默认值为 5ms。
    private double FRAME_SHIFT = 0.005;
    // 短时能量阈值 （短时能量的权重，标量，默认为 0.2）：用于调整短时能量阈值的权重。
    private double E = 0.2;
    // 短时过零率阈值 （短时过零率的权重，标量，默认为 0.96）：用于调整短时过零率阈值的权重。
    private double Z = 0.96;


    /**
     * 后端接收到文件
     * @param taskDTO
     */
    @Override
    @Async
    public void deal(TaskDTO taskDTO, AudioProcessingWebSocketHandler webSocketHandler, String taskId, File localFile) {

        //设置参数
        METHOD = taskDTO.getMETHOD();
        THRESHOLD = taskDTO.getTHRESHOLD();
        FLAGE = taskDTO.getFLAGE();
        FRAME_DURATION = taskDTO.getFRAME_DURATION();
        FRAME_SHIFT = taskDTO.getFRAME_SHIFT();
        E = taskDTO.getE();
        Z = taskDTO.getZ();
        //TODO 暂时设置flage为1，直接获取标签值
        //FLAGE = taskDTO.getFLAGE();

        long startTime = System.nanoTime();


        // 将mp3文件转为wav
        localFile = getFile(localFile);

        //创建数组的list存储被篡改的音频段起始时间（后续存入日志）
        List<Integer[]> results = null;
        // 判断方法类型
        String parameter = null;
        if (METHOD.equals(AlgorithmConstant.METHOD_CQCC)) {
            parameter = "阈值：" + THRESHOLD + "，标志值：" + FLAGE;
            results = getIntegersCQCC(webSocketHandler, taskId, startTime, localFile);
        }
        else if (METHOD.equals(AlgorithmConstant.METHOD_EZCS)) {
            parameter = "帧长：" + FRAME_DURATION + "，帧移：" + FRAME_SHIFT + "，短时能量阈值：" + E + "，短时过零率阈值：" + Z;
            results = getIntegersEZCSDA(webSocketHandler, taskId, startTime, localFile);
        }else {
            webSocketHandler.sendProgress(taskId + ": 参数错误，请重新选择算法!");
        }


        //存入日志
        User user = userMapper.getById(1L);
        Log log = Log.builder()
                .audioName(localFile.getName())
                .algorithm(METHOD)
                .parameter(parameter)
                .result(JSON.toJSONString(results))
                .userId(user.getId())
                .username(user.getUsername())
                .userPhone(user.getPhone())
                .creatTime(LocalDateTime.now())
                .Comment(taskDTO.getCOMMENT())
                .audioPath(localFile.getAbsolutePath())
                .build();
        userMapper.saveLog(log);
    }

    /**
     * 调用ezcsda算法
     * @param webSocketHandler
     * @param taskId
     * @param localFile
     * @param startTime
     * @return
     */
    private List<Integer[]> getIntegersEZCSDA(AudioProcessingWebSocketHandler webSocketHandler, String taskId, long startTime, File localFile) {
        List<Integer[]> results = new ArrayList<>(); //记录结果时间起始位置的list

        List<File> files = null; //创建存储切割后的文件的list

        //获取采样率
        float sampleRate = 0; //采样率变量
        try {
            AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(localFile);
            AudioFormat format = audioInputStream.getFormat();

            sampleRate = format.getSampleRate();
        } catch (UnsupportedAudioFileException | IOException e) {
            throw new RuntimeException(e);
        }

        // 分割文件
        try {
            SEGMENT_TIME_MS = 5000;
            files = ForgedDetectionUtil.splitAndSaveAudio(localFile, TEMP_DIR, SEGMENT_TIME_MS);
            log.info("文件分割完成");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        //存储返回给前端的结果
        Map<String, Object> map = new HashMap<>();

        extracted(webSocketHandler, taskId, files, sampleRate, results, map);

        // 删除分割后的临时文件
        for (File file1 : files) {
            file1.delete();
        }

        //返回结果
        map.put(taskId, results);
        webSocketHandler.sendProgress(JSON.toJSONString(map));
        log.info("算法运行时间：" + (System.nanoTime() - startTime) / 1000000000 + "s");
        return results;
    }

    /**
     * 提取结果
     * @param webSocketHandler
     * @param taskId
     * @param files
     * @param sampleRate
     * @param results
     * @param map
     */
    private void extracted(AudioProcessingWebSocketHandler webSocketHandler, String taskId, List<File> files, float sampleRate, List<Integer[]> results, Map<String, Object> map) {
        //调用matlab
        try {
            SplitDetection splitDetection = new SplitDetection();

            log.info("MATLAB初始化成功");
            for (int i = 0; i < files.size(); i++){ //遍历切割后的音频并循环调用matlab
                long oneTime = System.nanoTime();
                Object[] result = splitDetection.main(1, files.get(i).getAbsolutePath(), FRAME_DURATION, FRAME_SHIFT, E, Z); //调用ezcsda算法
                MWLogicalArray resultArray = (MWLogicalArray) result[0]; //转类型

                boolean[] resultData = (boolean[]) resultArray.getData(); //得到boolean数组
                int[] re = new int[resultData.length]; // 创建存储1或0值数组
                for (int j = 0; j < resultData.length; j++) { //获取掩码矩阵
                    re[j] = resultData[j] ? 1 : 0;
                }

                //计算起始时间
                boolean inRegion = false;
                int startIndex = 0;
                for (int j = 0; j < re.length; j++) {
                    if (re[j] == 1 && !inRegion) {
                        startIndex = j;
                        inRegion = true;
                    } else if (re[j] == 0 && inRegion) {
                        inRegion = false;
                        int endIndex = j - 1;
                        int startTimeMs = (int) (startIndex * 1000 / sampleRate);
                        int endTimeMs = (int) (endIndex * 1000 / sampleRate);
                        results.add(new Integer[]{startTimeMs, endTimeMs});
                    }
                }
                //计算处理进度
                double process = (double) (i + 1) / files.size();

                //存储进度返回结果
                map.put(taskId, process);
                webSocketHandler.sendProgress(JSON.toJSONString(map));
                log.info("第{}段音频处理完成，耗时{}ms", i + 1, (System.nanoTime() - oneTime) / 1000000);
            }

        } catch (MWException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 调用cqcc算法
     * @param webSocketHandler
     * @param taskId
     * @param startTime
     * @param localFile
     * @return
     */
    private static List<Integer[]> getIntegersCQCC(AudioProcessingWebSocketHandler webSocketHandler, String taskId, long startTime, File localFile) {
        List<File> files = null;

        // 分割文件
        try {
            files = ForgedDetectionUtil.splitAndSaveAudio(localFile, TEMP_DIR, SEGMENT_TIME_MS);
            log.info("文件分割完成");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        // 创建二维数组存储标签调用matlab 0篡改 1原始
        int[][] labels = extracted(files, webSocketHandler, taskId);


        // 删除分割后的临时文件
        for (File file1 : files) {
            file1.delete();
        }

        List<Integer[]> results = getResults(files, labels);

        long endTime = System.nanoTime();

        //返回前端发送结果
        Map<String, Object> map = new HashMap<>();
        map.put(taskId, results);
        webSocketHandler.sendProgress(JSON.toJSONString(map));

        log.info("总耗时: {} ms", (endTime - startTime) / 1_000_000.0);
        return results;
    }

    /**
     * 处理矩阵获取结果
     * @param files
     * @param labels
     * @return
     */
    private static List<Integer[]> getResults(List<File> files, int[][] labels) {
        // 处理矩阵
        int[] re = new int[files.size()]; //将段中篡改的段值改为1
        for (int i = 0; i < 10; i++) {
            for (int j = i + 1; j < 10; j++){
                if (labels[i][j] == 1){
                    re[i] = 1;
                    re[j] = 1;
                }
            }
        }

        List<Integer[]> results = new ArrayList<>();
        // 遍历数组
        for (int i = 0; i < re.length; i++) { //提取连续的子数组
            if (re[i] == 1) {
                int start = i;
                int sTime = SEGMENT_TIME_MS * (start);

                // 找到连续的1的段
                while (i < re.length && re[i] == 1) {
                    i++;
                }

                // 记录该段的起始和结束下标
                int end = i - 1;
                int eTime = SEGMENT_TIME_MS * (end + 1);

                // 将这段记录到结果列表中
                results.add(new Integer[]{sTime, eTime});
            }
        }
        return results;
    }


    /**
     * 调用 MATLAB 函数
     * @param files
     */
    private static int[][] extracted(List<File> files, AudioProcessingWebSocketHandler webSocketHandler, String taskId) {
        int[][] labels = new int[files.size()][files.size()];

        try {
            long detectionStartTime = System.nanoTime();
            //ForgedDetection detector = new ForgedDetection();
            Class1 detector = new Class1();
            log.info("MATLAB 函数初始化成功！");

            // 滑动窗口调用算法
            log.info("开始鉴别");
            int count = 0;
            Map<String, Object> map = new HashMap<>();
            for (int i = 0; i < files.size()-1; i++) {
                for (int j = i + 1; j < files.size(); j++) {
                    long detectionStartTime1 = System.nanoTime();
                    // 调用 MATLAB 函数
                    Object[] result = detector.main(1,
                            files.get(i).getAbsolutePath(),
                            files.get(j).getAbsolutePath(),
                            i,
                            j,
                            96.0,
                            METHOD,
                            THRESHOLD,
                            FLAGE);

                    // 判断是否为篡改 篡改为1 未被篡改为0
                    if (ResultFlag.FORGED.equals(result[0])){
                        labels[i][j] = 1;
                    } else if (ResultFlag.ORIGINAL.equals(result[0]) && labels[i][j] == 1)
                        labels[i][j] = 0;

                    log.info("{},{} PCC 为：{}", i, j, result[0]);

                    // 计算进度
                    count++;
                    double progress = count * 2 / (double)((files.size()-1) * files.size());
                    map.put(taskId, progress);
                    // 通过 WebSocket 向前端发送进度
                    webSocketHandler.sendProgress(JSON.toJSONString(map));

                    long detectionEndTime = System.nanoTime();
                    log.info("鉴别耗时: {} ms", (detectionEndTime - detectionStartTime1) / 1_000_000.0);
                    // TODO 返回结果
                }
            }
            detector.dispose();

            long detectionEndTime = System.nanoTime();
            log.info("鉴别耗时: {} ms", (detectionEndTime - detectionStartTime) / 1_000_000.0);
            return labels;
        } catch (MWException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 将mp3文件转为wav
     * @param localFile
     * @return
     */
    private static File getFile(File localFile) {
        // 获取原始文件后缀
        String originalFilename = localFile.getName();
        // 判空
        if (originalFilename != null) {
            // 获取后缀
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            // 获取文件名
            String fileNameWithoutSuffix = originalFilename.substring(0, originalFilename.lastIndexOf("."));
            // 如果是 mp3 文件，转换为 wav 并删除 mp3
            if (suffix.equals(".mp3")) {
                try {
                    // 拼接文件路径
                    String outputFilePath = UPLOAD_DIR + fileNameWithoutSuffix + ".wav";
                    // 调用转换方法
                    ForgedDetectionUtil.convertAudioFormat(localFile.getAbsolutePath(), outputFilePath, "wav");
                    log.info("mp3 文件成功转换为 wav: {}", localFile);
                    // 删除 原mp3，留下转换后wav文件
                    File wavFile = localFile;
                    localFile = new File(outputFilePath);
                    wavFile.delete();
                    log.info("mp3 文件删除成功:{}", wavFile);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return localFile;
    }


    /**
     * 保存在本地
     * @param multipartFile 前端传入的文件
     * @return 保存的文件
     */
    public static File saveFile(MultipartFile multipartFile) {
        // 检查文件是否为空
        if (multipartFile.isEmpty()) {
            throw new RuntimeException("文件不能为空");
        }

        // 获取原始文件名
        String originalFilename = multipartFile.getOriginalFilename();

        // 设置文件保存路径（可以根据需要自定义路径）
        String filePath = UPLOAD_DIR + originalFilename;
        if (!new File(UPLOAD_DIR).exists())
            new File(UPLOAD_DIR).mkdirs();

        // 保存文件到本地
        try {
            // 使用 transferTo 方法保存文件
            multipartFile.transferTo(new File(filePath));
            log.info("文件保存成功，路径：{}", filePath);
            return new File(filePath);
        } catch (IOException e) {
            throw new RuntimeException("保存文件失败", e);
        }
    }
}
